package com.nado.extensions.data;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.nado.extensions.reload.AbstractFileReloadable;
import com.nado.extensions.util.ResourceLoader;

import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Pattern;
import java.util.List;

/**
 * {@link DataHandler}의 타입 변환 API를 구현하고 있는 추상 클래스.
 * <p/>
 * 타입 변환 API는 해당 타입으로 변환할 노드의 키 값을 인수로 받으며,
 * 해당 노드가 존재하지 않거나 타입 변환에 실패한 경우에 발생하는 Exception 대신 반환되는 default 값을 추가적으로 지정할 수 있다.
 * <p/>
 * 숫자 타입으로의 변환은 아래와 같은 형태의 API 호출을 통해 이루어진다.
 * <pre><code>
 * int integer = Integer.parseInt(String value);
 * long long = Long.parseLong(String value);
 * float float = Float.parseFloat(String value);
 * double double = Double.parseDouble(String value);
 * </pre></code>
 *
 */
public abstract class AbstractDataHandler extends AbstractFileReloadable implements DataHandler {

	protected static final Pattern pattern = Pattern.compile("\\$\\{[^\\}]*\\}");   // share

	protected Log log = LogFactory.getLog(DataHandler.class);

	protected String name;
	protected String location;

	public AbstractDataHandler() {
		super(false);
	}

	public AbstractDataHandler(String location, boolean reload) {
		super(reload);
		this.location = location;
	}

	/**
	 * 설정된 위치 {@code location}의 XML 파일을 읽는다.
	 */
	protected abstract void initialize();

	protected InputStream getInputStream(String location, ResourceLoader loader) throws IOException {
		InputStream inputStream = loadFile(location, loader);
		if (inputStream == null) {
			throw new DataHandlerException("Invalid XMLData file path. " + location + " does not exist.");
		}
		return inputStream;
	}

	public String getHandlerName() {
		return name;
	}

	public String getLocation() {
		return location;
	}

	public DataHandler subConfig(String key) {
		return peek(key);
	}

	public List<DataHandler> sublists(String path) {
		return peekAll(path);
	}

	public List<DataHandler> childConfigs() throws DataHandlerException {
		return selectChildren();
	}

	public DataHandler subconfig(String key) throws DataHandlerException {
		return select(key);
	}

	public List<DataHandler> subConfigs(String key) throws DataHandlerException {
		return selectAll(key);
	}

	public List<DataHandler> selectChildren() {
		List<DataHandler> children = peekChildren();
		if (children.isEmpty()) {
			throw new DataHandlerException(getRootName() + " don't have children");
		}
		return children;
	}

	public void setHandlerName(String name) {
		this.name = name;
	}

	public void setLocation(String location) {
		this.location = location;
	}

	public String get(String key, String defaultValue) {
		String value;
		try {
			value = get(key);
		} catch (DataHandlerException e) {
			value = defaultValue;
		}
		return value;
	}

	public boolean getBoolean(String key) {
		return getBoolean(key, false);
	}

	public boolean getBoolean(String key, boolean defaultValue) {
		String value;

		try {
			value = get(key);
		} catch (DataHandlerException e) {
			return defaultValue;
		}

		if ("true".equalsIgnoreCase(value)) {
			return true;
		} else if ("false".equalsIgnoreCase(value)) {
			return false;
		}

		return defaultValue;
	}

	public int getInt(String key) {
		return Integer.parseInt(get(key));
	}

	public int getInt(String key, int defaultValue) {
		int value;
		try {
			value = getInt(key);
		} catch (DataHandlerException e) {
			value = defaultValue;
		}
		return value;
	}

	public double getDouble(String key) {
		return Double.parseDouble(get(key));
	}

	public double getDouble(String key, double defaultValue) {
		double value;
		try {
			value = getDouble(key);
		} catch (DataHandlerException e) {
			value = defaultValue;
		}
		return value;
	}

	public long getLong(String key) {
		return Long.valueOf(get(key));
	}

	public long getLong(String key, long defaultValue) {
		long value;
		try {
			value = getLong(key);
		} catch (DataHandlerException e) {
			value = defaultValue;
		}
		return value;
	}

	public float getFloat(String key) {
		return Float.parseFloat(get(key));
	}

	public float getFloat(String key, float defaultValue) {
		float value;
		try {
			value = getFloat(key);
		} catch (DataHandlerException e) {
			value = defaultValue;
		}
		return value;
	}

	public boolean load() {
		initialize();
		return true;
	}
}